Source code for hysop.backend.device.codegen.base.function_codegen

# Copyright (c) HySoP 2011-2024
#
# This file is part of HySoP software.
# See "https://particle_methods.gricad-pages.univ-grenoble-alpes.fr/hysop-doc/"
# for further info.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.


import pyopencl as cl
from contextlib import contextmanager

from hysop.tools.htypes import check_instance
from hysop.backend.device.codegen.base.codegen import CodeGenerator
from hysop.backend.device.codegen.base.opencl_codegen import OpenClCodeGenerator
from hysop.backend.device.codegen.base.utils import ArgDict
from hysop.backend.device.opencl.opencl_types import TypeGen, OpenClTypeGen
from hysop.backend.device.codegen.base.variables import (
    CodegenVariable,
    CodegenVectorClBuiltin,
    CodegenVectorClBuiltinFunc,
)


[docs] class FunctionBase: def __init__( self, fname, output, fargs, known_args, typegen, symbolic_mode=None, inline=False, **kargs, ): super().__init__(typegen=typegen, **kargs) known_args = ArgDict() if (known_args is None) else known_args output = "void" if (output is None) else output check_instance(fargs, ArgDict) fargs.release() for varname, varval in known_args.items(): if varname in fargs.keys(): if isinstance(varval, CodegenVariable): if not varval.known(): pass else: varval.const = True if symbolic_mode: varval.force_symbolic(symbolic_mode) fargs[varname] = varval else: try: fargs[varname].set_value(varval) except ValueError: var = fargs[varname] print() print( f"FATAL ERROR: Failed to set value of known variable {varname}." ) print(f" *variable type was {type(var).__name__}") if hasattr(var, "dim"): print(f" *variable dim was {var.dim}") print(f" *value was of type {type(varval)}") print(f" *value was {varval}") print() raise fargs[varname].const = True if symbolic_mode: fargs[varname].force_symbolic(symbolic_mode) fargs.lock() self.fname = fname self.output = output self.inline = inline self.args = fargs self.known_args = known_args def __call__(self, *var, **kvars): return f"{self.fname}({self.match_args(*var,**kvars)})" def _return(self, what): code = f"return {what};" self.append(code)
[docs] def match_args(self, *vars, **kvars): args = self.args arg_order = args.arg_order sargs = [] for argname in arg_order: arg = args[argname] if not arg.known(): if len(vars) > 0: var = vars.pop(0) elif argname in kvars: var = kvars[argname] else: svars = "".join([f"\n\t{var}" for var in sorted(vars)]) skvars = "".join( [ f"\n\t{kname} -> {kvars[kname]}" for kname in sorted(kvars.keys()) ] ) if len(svars) != 0: svars += "\n " if len(skvars) != 0: skvars += "\n " msg = """Could not match function argument '{}' of type '{}' while calling function '{}' from: vars = [{}] kvars = {}{}{} """.format( argname, arg.ctype, self.fname, svars, "{", skvars, "}" ) raise RuntimeError(msg) if isinstance(var, str): sargs.append(var) elif isinstance(var, arg.__class__) or ( arg.__class__ == CodegenVectorClBuiltin and arg.dim == 1 and isinstance(var, (CodegenVariable, str)) ): sargs.append(var.name) else: msg = "Matched variable '{}' with argument '{}' but there was a type mismatch('{}' is not an instance of '{}')!".format( getattr(var, "name", "unknown"), arg.name, var.__class__, arg.__class__, ) raise RuntimeError(msg) return ", ".join(sargs)
[docs] class FunctionCodeGenerator(FunctionBase, CodeGenerator): def __init__( self, basename, typegen, output, args=None, known_args=None, inline=False, ext=".tmp", ): check_instance(typegen, TypeGen) name = basename fname = basename if args is not None: check_instance(args, ArgDict) fname += args.function_name_suffix(output, known_args) name += args.codegen_name_suffix(output, known_args) super().__init__( name=name, typegen=typegen, fname=fname, output=output, inline=inline, ext=ext, fargs=args, known_args=known_args, ) self.inject_vars(args) @contextmanager def _function_(self): name = self.fname output = self.output fargs, fargs_impl, cargs = self.args.build_args() with super()._function_( name=name, output=output, args=fargs, args_impl=fargs_impl, inline=self.inline, ) as f: for c in cargs: self.append(c) yield f
[docs] class OpenClFunctionCodeGenerator(FunctionBase, OpenClCodeGenerator): def __init__( self, basename, typegen, output, args=None, known_args=None, inline=False, ext=".cl", ): check_instance(typegen, OpenClTypeGen) name = basename fname = basename if args is not None: check_instance(args, ArgDict) fname += args.function_name_suffix(output, known_args) name += args.codegen_name_suffix(output, known_args) super().__init__( name=name, typegen=typegen, fname=fname, output=output, inline=inline, ext=ext, fargs=args, known_args=known_args, ) self.inject_vars(args) @contextmanager def _function_(self): name = self.fname output = self.output fargs, fargs_impl, cargs = self.args.build_args() with super()._function_( name=name, output=output, args=fargs, args_impl=fargs_impl, inline=self.inline, ) as f: for c in cargs: c.declare(self) yield f